﻿// <copyright file="EDXLDEDal.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// "EDXLDEDal".cs - "Data Access Layer Library for EDXL-DE"
// Project: IC.NET SOA - IC.NET DAL
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using EDXLSharp;
using EDXLSharp.EDXLDELib;
using ICNETServices;
using EDXLCoT;
using CoT_Library.Details;

namespace ICNETDAL
{
  /// <summary>
  /// Contains several methods that can add, delete, or retrieve objects from the database
  /// </summary>
  public class EDXLDEDal
  {
    #region Public Member Functions

    /// <summary>
    /// Inserts an emergency message container into the database
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <returns>An emergency message container identifier</returns>
      public int AddToDB(EDXLDE distributionElementObject)
      {
          TimeSpan staleTime = new TimeSpan(0, ICNETSOAConstants.CacheClearDurationInMinutes, 0);

          if (ICNETSOAConstants.CacheClearEnabled &&
              DateTime.UtcNow.Subtract(distributionElementObject.DateTimeSent).CompareTo(staleTime) >= 0)
          {
              distributionElementObject.DistributionType = TypeValue.Cancel;
          }

          EmergencyMessageContainer emergencyMessageContainer;
          emergencyMessageContainer = this.CreateEmergencyMessageContainer(distributionElementObject);
          EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
          linqDB.EmergencyMessageContainers.InsertOnSubmit(emergencyMessageContainer);
          linqDB.SubmitChanges();

          /*
        if (ICNETSOAConstants.CacheClearEnabled)
        {
          GeoRSSDal.DeleteStaleExposureCacheEntries();
        }*/


          //Now remove the stale cancel messages
          DeleteStaleCancelledMessages();

          return emergencyMessageContainer.Id;
      }

      /// <summary>
      /// Remove any cancel messages from the database that are past the allotted retainer time
      /// </summary>
    public void DeleteStaleCancelledMessages()
    {
        TimeSpan cancelRetain = new TimeSpan(0, ICNETSOAConstants.CancelMessageRetainInMinutes, 0);
        EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
        var cancelMessageContainer = linqDB.EmergencyMessageContainers.Where(
            ccl =>
                ((DateTime.UtcNow - ccl.DateTimeSent) >= cancelRetain)
                && (ccl.DistributionType.Equals(TypeValue.Cancel.ToString())) );
                
        if (cancelMessageContainer != null)
        {
            linqDB.EmergencyMessageContainers.DeleteAllOnSubmit(cancelMessageContainer);
            linqDB.SubmitChanges();
        }
    }

    /// <summary>
    ///   Deletes a emergency message container from the database
    /// </summary>
    /// <param name="senderCode">Code that uniquely identifies the sender of the distribution message</param>
    /// <param name="distributionCode">Code that uniquely identifies by sender a distribution message</param>
    public void DeleteEmergencyMessageContainer(string senderCode, string distributionCode)
    {
      EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
      var emergencyMessageContainer = linqDB.EmergencyMessageContainers.SingleOrDefault(
        emc => emc.SenderCode == senderCode && emc.DistributionCode == distributionCode);     // NOTE: Need unique index on senderCode and distributionCode (i.e., natural key for EmergencyMessageConstainer)
      if (emergencyMessageContainer != null)
      {
        linqDB.EmergencyMessageContainers.DeleteOnSubmit(emergencyMessageContainer);
        linqDB.SubmitChanges();
      }
    }

    /// <summary>
    /// Queries the database to get a content object. 
    /// </summary>
    /// <param name="hashCode"> the hashcode that refers to the content object</param>
    /// <returns>Null if the hash is not in the database,  or the XML Element if it was  found</returns>
    public XElement GetContentObjectByHash(int hashCode)
    {
      EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
      EmergencyMessageContent emergencyMessageContent = null;

      try
      {
        emergencyMessageContent = (from p in linqDB.EmergencyMessageContents
                                   where p.DSCHash == hashCode
                                   select p).FirstOrDefault();
      }
      catch (InvalidOperationException e)
      {
        Console.WriteLine("Bad Content Key: " + hashCode + " : " + e.Message + "\n" + e.StackTrace);
        return null; // hash is not in the database
      }

      if (emergencyMessageContent != null && emergencyMessageContent.XMLContents.Count > 0)
      {
        var xml = (from p in linqDB.XMLContents
                   where p.EmergencyMessageContentId == emergencyMessageContent.Id
                   select p.EmbeddedXMLContent).SingleOrDefault();
        if (xml != null)
        {
          XElement xe = (XElement)xml;
          return xe;
        }
        else
        {
          return null; // could not get single item
        }
      }
      else
      {
        return null; // object is in database, but has no xml content
      }
    }

    /// <summary>
    /// Queries the database to get a DE object. 
    /// </summary>
    /// <param name="DEId">DE Distribution Id</param>
    /// <param name="Sender">DE Sender Id</param>
    /// <returns>Null if the DE is not in the database,  or the XML Element if it was found</returns>
    public XElement GetDE(string DEId, string Sender)
    {
      EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
      EmergencyMessageContainer de = null;

      try
      {
        de = (from p in linqDB.EmergencyMessageContainers
                                   where p.DistributionCode == DEId && p.SenderCode == Sender
                                   select p).FirstOrDefault();
      }
      catch (InvalidOperationException)
      {
        return null; // hash is not in the database
      }

      if (de != null)
      {
        return de.DEXML;
      }
      else
      {
        return null; // object is in database, but has no xml content
      }
    }

    /// <summary>
    /// Queries the database to get non xml data. 
    /// </summary>
    /// <param name="hashCode"> the hashcode that refers to the content object</param>
    /// <returns>Null if the hash is not in the database,  or the data if it was found</returns>
    public byte[] GetNonXMLContentObjectByHash(int hashCode)
    {
      EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
      EmergencyMessageContent emergencyMessageContent = null;
      emergencyMessageContent = (from p in linqDB.EmergencyMessageContents
                                 where p.DSCHash == hashCode
                                 select p).FirstOrDefault();
      if (emergencyMessageContent != null && emergencyMessageContent.NonXMLContents.Count > 0)
      {
        var xml = (from p in linqDB.NonXMLContents
                   where p.EmergencyMessageContentId == emergencyMessageContent.Id
                   select p.BinaryContentData).SingleOrDefault();
        if (xml != null)
        {
          return xml.ToArray();
        }
        else
        {
          return null;
        }
      }
      else
      {
        return null;
      }
    }

    /// <summary>
    /// Queries the database to get non XML MIME data.
    /// </summary>
    /// <param name="hashCode"> the hashcode that refers to the content object</param>
    /// <returns>Null if the hash is not in the database,  or the data if it was found</returns>
    public string GetNonXMLMIMETypeByHash(int hashCode)
    {
      EDXLSQLDataContext linqDb = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
      var contentObjects = (from p in linqDb.EmergencyMessageContents
                            where p.DSCHash == hashCode
                            select p).FirstOrDefault();
      if (contentObjects != null && contentObjects.NonXMLContents.Count > 0)
      {
        var mime = (from p in linqDb.NonXMLContents
                    where p.EmergencyMessageContentId == contentObjects.Id
                    select p).FirstOrDefault();
        return mime != null ? mime.MIMEType : string.Empty;
      }
      else
      {
        return string.Empty;
      }
    }
    #endregion

    #region Private Member Functions

    /// <summary>
    ///   Creates an emergency message container object
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <returns>The emergency message container created</returns>
    private EmergencyMessageContainer CreateEmergencyMessageContainer(EDXLDE distributionElementObject)
    {
      EmergencyMessageContainer emergencyMessageContainer = new EmergencyMessageContainer();
      emergencyMessageContainer.DistributionCode = distributionElementObject.DistributionID;
      emergencyMessageContainer.CombinedConfidentiality = distributionElementObject.CombinedConfidentiality;
      emergencyMessageContainer.DateTimeSent = distributionElementObject.DateTimeSent;
      emergencyMessageContainer.DistributionStatus = distributionElementObject.DistributionStatus.ToString();
      emergencyMessageContainer.DistributionType = distributionElementObject.DistributionType.ToString();
      emergencyMessageContainer.Language = distributionElementObject.Language;
      emergencyMessageContainer.SenderCode = distributionElementObject.SenderID;
      
      this.AddDistributionReferencesToEmergencyMessageContainer(distributionElementObject, emergencyMessageContainer);
      this.AddValueListsToEmergencyMessageContainer(distributionElementObject, emergencyMessageContainer);
      this.AddValueSchemesToEmergencyMessageContainer(distributionElementObject, emergencyMessageContainer);
      this.AddTargetAreasToEmergencyMessageContainer(distributionElementObject, emergencyMessageContainer);
      this.AddContentObjectsToEmergencyMessageContainer(distributionElementObject, emergencyMessageContainer);

      RemoveICNETRouterRoles(distributionElementObject);

      emergencyMessageContainer.DEXML = XElement.Parse(distributionElementObject.WriteToXML());

      return emergencyMessageContainer;
    }

    /// <summary>
    /// Removes any routing roles added by ICNET
    /// </summary>
    /// <param name="distElemObj">An object serialized from an EDXL document</param>
    private void RemoveICNETRouterRoles(EDXLDE distElemObj)
    {
      foreach (ContentObject co in distElemObj.ContentObjects)
      {
        for (int i = co.ConsumerRole.Count - 1; i >= 0; i--)
        {
          if (co.ConsumerRole[i].ValueListURN.Contains("icnet-http"))
          {
            co.ConsumerRole.RemoveAt(i);
          }
        }
      }
    }

    /// <summary>
    ///   Adds distribution reference objects to an emergency message container object
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContainer">An emergency message container object</param>
    private void AddDistributionReferencesToEmergencyMessageContainer(EDXLDE distributionElementObject, EmergencyMessageContainer emergencyMessageContainer)
    {
      DistributionReference distributionReference;
      foreach (string reference in distributionElementObject.DistributionReference)
      {
        distributionReference = new DistributionReference();
        distributionReference.DistributionReference1 = reference;
        emergencyMessageContainer.DistributionReferences.Add(distributionReference);
      }
    }

    /// <summary>
    ///   Adds the following to an emergency message container object:
    ///     Sender role objects
    ///     Recipient objects
    ///     Container objects
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContainer">An emergency message container object</param>
    private void AddValueListsToEmergencyMessageContainer(EDXLDE distributionElementObject, EmergencyMessageContainer emergencyMessageContainer)
    {
      SenderRole senderRole;
      RecipientRole recipientRole;
      ContainerTopic containerTopic;
      foreach (ValueList tempList in distributionElementObject.SenderRole)
      {
        foreach (string s in tempList.Value)
        {
          senderRole = new SenderRole();
          senderRole.ListURI = tempList.ValueListURN;
          senderRole.Value = s;
          emergencyMessageContainer.SenderRoles.Add(senderRole);
        }
      }

      foreach (ValueList tempList in distributionElementObject.RecipientRole)
      {
        foreach (string s in tempList.Value)
        {
          recipientRole = new RecipientRole();
          recipientRole.ListURI = tempList.ValueListURN;
          recipientRole.Value = s;
          emergencyMessageContainer.RecipientRoles.Add(recipientRole);
        }
      }

      foreach (ValueList tempList in distributionElementObject.Keyword)
      {
        foreach (string s in tempList.Value)
        {
          containerTopic = new ContainerTopic();
          containerTopic.ListURI = tempList.ValueListURN;
          containerTopic.Value = s;
          emergencyMessageContainer.ContainerTopics.Add(containerTopic);
        }
      }
    }

    /// <summary>
    ///   Adds recipient objects to an emergency message container object
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContainer">An emergency message container object</param>
    private void AddValueSchemesToEmergencyMessageContainer(EDXLDE distributionElementObject, EmergencyMessageContainer emergencyMessageContainer)
    {
      Recipient recipient;
      foreach (ValueScheme tempScheme in distributionElementObject.ExplicitAddress)
      {
        foreach (string s in tempScheme.ExplicitAddressValue)
        {
          recipient = new Recipient();
          recipient.Value = s;
          recipient.ExplicitAddressScheme = tempScheme.ExplicitAddressScheme;
          emergencyMessageContainer.Recipients.Add(recipient);
        }
      }
    }

    /// <summary>
    ///   Adds target area objects to an emergency message container object
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContainer">An emergency message container object</param>
    private void AddTargetAreasToEmergencyMessageContainer(EDXLDE distributionElementObject, EmergencyMessageContainer emergencyMessageContainer)
    {
      TargetArea targetArea;

      // int index;
      // double lat,lon,radius;
      string pointstr;
      char[] separator = { ' ' };
      string[] points;
      foreach (TargetAreaType theTargetArea in distributionElementObject.TargetArea)
      {
        foreach (string s in theTargetArea.Circle)
        {
          targetArea = new TargetArea();
          targetArea.Circle = s;

          // index = s.LastIndexOf(" ");
          // radius=Double.Parse(s.Substring(index+1));
          // pointstr = s.Substring(0, index);
          // radius *= 1000;
          // index=pointstr.IndexOf(",");
          // lat = Double.Parse(pointstr.Substring(0, index));
          // lon = Double.Parse(pointstr.Substring(index + 1));
          emergencyMessageContainer.TargetAreas.Add(targetArea);
        }

        foreach (string s in theTargetArea.Country)
        {
          targetArea = new TargetArea();
          targetArea.Country = s;
          emergencyMessageContainer.TargetAreas.Add(targetArea);
        }

        foreach (string s in theTargetArea.LocCodeUN)
        {
          targetArea = new TargetArea();
          targetArea.UNLocCode = s;
          emergencyMessageContainer.TargetAreas.Add(targetArea);
        }

        foreach (string s in theTargetArea.Polygon)
        {
          // 42,-124.2102 42,-120.1 39,-120 35.0,-114.6328 34.35,- 120.4418 38.9383,-123.817 42,-124.2102
          targetArea = new TargetArea();
          points = s.Split(separator);

          StringBuilder sb = new StringBuilder();

          if (points.Length == 2)
          {
            sb.Append("POINT(");
            sb.Append(DEDalUtils.SwapLatLon(points[0], true));
            sb.Append(")");
          }
          else
          {
            sb.Append("POLYGON((");
            for (int i = points.Count() - 1; i != -1; i--)
            {
              pointstr = points[i];
              sb.Append(DEDalUtils.SwapLatLon(pointstr, true));
              sb.Append(" , ");
            }

            sb.Remove(sb.Length - 3, 3);
            sb.Append("))");
          }
          targetArea.Location = sb.ToString();
          emergencyMessageContainer.TargetAreas.Add(targetArea);
        }
      }
    }

    /// <summary>
    ///   Adds emergency message content objects to an emergency message container object
    /// </summary>
    /// <param name="distributionElementObject">An object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContainer">An emergency message container object</param>
    //// private void ContentObjectsToDB(EDXLSQLDataContext LinQDB, EDXLDE DEObj, EmergencyMessageContainer SqlDEObj, List<int> ContentObjectKeys)
    private void AddContentObjectsToEmergencyMessageContainer(EDXLDE distributionElementObject, EmergencyMessageContainer emergencyMessageContainer)
    {
      EmergencyMessageContent emergencyMessageContent;
      foreach (ContentObject co in distributionElementObject.ContentObjects)
      {
        emergencyMessageContent = new EmergencyMessageContent();
        emergencyMessageContent.Confidentiality = co.Confidentiality;
        emergencyMessageContent.ContentDescription = co.ContentDescription;
        emergencyMessageContent.IncidentDescription = co.IncidentDescription;
        emergencyMessageContent.IncidentCode = co.IncidentID;

        emergencyMessageContent.Type = co.XMLContent != null ? "XML" : "NonXML";

        string dscString = distributionElementObject.DistributionID + distributionElementObject.SenderID + co.ContentDescription;
        byte[] b2 = System.Text.Encoding.ASCII.GetBytes(dscString);
        emergencyMessageContent.DSCHash = ComputeHash(b2);

        emergencyMessageContainer.EmergencyMessageContents.Add(emergencyMessageContent);

        this.AddContentObjectValueListsToEmergencyMessageContent(co, emergencyMessageContent);
        this.AddContentPayloadsToEmergencyMessageContent(co, emergencyMessageContent);
        this.AddExposureCacheObjectsToEmergencyMessageContent(co, emergencyMessageContent, emergencyMessageContainer);
      }
    }

    private int ComputeHash(byte[] data)
    {
      unchecked
      {
        const int p = 16777619;
        int hash = (int)2166136261;

        for (int i = 0; i < data.Length; i++)
          hash = (hash ^ data[i]) * p;

        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        return hash;
      }
    } 

    /// <summary>
    ///   Adds the following to emergency message content object:
    ///     Consumer role objects
    ///     Content topic objects
    ///     Originator role objects
    /// </summary>
    /// <param name="co">Content object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContent">Emergency message content object</param>
    private void AddContentObjectValueListsToEmergencyMessageContent(ContentObject co, EmergencyMessageContent emergencyMessageContent)
    {
      ConsumerRole consumerRole;
      ContentTopic contentTopic;
      OriginatorRole originatorRole;
      foreach (ValueList role in co.ConsumerRole)
      {
        //don't add icnet routing roles to the DE that is being stored in the DB
        if (!role.ValueListURN.Contains("icnet-http"))
        {
          foreach (string s in role.Value)
          {
            consumerRole = new ConsumerRole();
            consumerRole.Value = s;
            consumerRole.ListURI = role.ValueListURN;
            emergencyMessageContent.ConsumerRoles.Add(consumerRole);
          }
        }
      }

      foreach (ValueList keyword in co.ContentKeyword)
      {
        foreach (string s in keyword.Value)
        {
          contentTopic = new ContentTopic();
          contentTopic.Value = s;
          contentTopic.ListURI = keyword.ValueListURN;
          emergencyMessageContent.ContentTopics.Add(contentTopic);
        }
      }

      foreach (ValueList role in co.OriginatorRole)
      {
        foreach (string s in role.Value)
        {
          originatorRole = new OriginatorRole();
          originatorRole.Value = s;
          originatorRole.ListURI = role.ValueListURN;
          emergencyMessageContent.OriginatorRoles.Add(originatorRole);
        }
      }
    }

    /// <summary>
    ///   Adds the following to emergency message content object:
    ///     Non xml content objects
    ///     XML content objects
    ///     Other objects
    /// </summary>
    /// <param name="co">Content object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContent">Emergency message content object</param>
    private void AddContentPayloadsToEmergencyMessageContent(ContentObject co, EmergencyMessageContent emergencyMessageContent)
    {
      Other other;
      if (co.NonXMLContent != null)
      {
        NonXMLContent nonXMLContent = new NonXMLContent();
        nonXMLContent.BinaryContentID = Guid.NewGuid();
        nonXMLContent.Digest = co.NonXMLContent.Digest;
        nonXMLContent.MIMEType = co.NonXMLContent.MIMEType;

        if (co.NonXMLContent.ContentData != null)
        {
          nonXMLContent.BinaryContentData = Convert.FromBase64String(co.NonXMLContent.ContentData);

          nonXMLContent.Size = co.NonXMLContent.Size == nonXMLContent.BinaryContentData.Length ? (int)co.NonXMLContent.Size : nonXMLContent.BinaryContentData.Length;
        }
        else
        {
          nonXMLContent.Size = 0;
        }

        if (co.NonXMLContent.URI != null)
        {
          nonXMLContent.Uri = co.NonXMLContent.URI.ToString();
        }

        emergencyMessageContent.NonXMLContents.Add(nonXMLContent);
      }
      else if (co.XMLContent != null)
      {
        XMLContent xmlContent;
        foreach (XElement xdoc in co.XMLContent.KeyXMLContent)
        {
          xmlContent = new XMLContent();
          xmlContent.KeyXMLContent = xdoc;
          emergencyMessageContent.XMLContents.Add(xmlContent);
        }

        foreach (XElement xdoc in co.XMLContent.EmbeddedXMLContent)
        {
          xmlContent = new XMLContent();
          xmlContent.EmbeddedXMLContent = xdoc;
          emergencyMessageContent.XMLContents.Add(xmlContent);
        }
      }

      foreach (XElement xe in co.Other)
      {
        other = new Other();
        other.Signature = xe;
        emergencyMessageContent.Others.Add(other);
      }
    }

    /// <summary>
    ///   Adds exposure cache objects to an emergency message content object
    /// </summary>
    /// <param name="contentObject">Content object serialized from an EDXL document</param>
    /// <param name="emergencyMessageContent">Emergency message content object</param>
    /// <param name="emergencyMessageContainer">Emergency message container object</param>
    private void AddExposureCacheObjectsToEmergencyMessageContent(ContentObject contentObject, EmergencyMessageContent emergencyMessageContent, EmergencyMessageContainer emergencyMessageContainer)
    {
      List<SyndicationItem> syndicationItems;
      bool contentHasXMLContent = contentObject.XMLContent != null;
      bool containerHasTargetArea = emergencyMessageContainer.TargetAreas.Count() > 0;

      if (contentHasXMLContent)
      {
        syndicationItems = GeoRSSDal.GetXMLContentSyndicationItems(contentObject);
      }
      else
      {
        syndicationItems = GeoRSSDal.GetNonXMLContentSyndicationItems(contentObject);
      }

      foreach (SyndicationItem syndicationItem in syndicationItems)
      {
        ExposureCache exposureCache = new ExposureCache();
        exposureCache.Author = syndicationItem.Authors[0].Name;
        exposureCache.BaseUri = ICNETSOAConstants.ICNETbaseURI;
        exposureCache.Content = ((TextSyndicationContent)syndicationItem.Content).Text;
        exposureCache.Copyright = syndicationItem.Copyright.Text;
        exposureCache.LastUpdatedTime = syndicationItem.LastUpdatedTime.DateTime;
        exposureCache.PublishDate = syndicationItem.PublishDate.DateTime;
        exposureCache.Summary = syndicationItem.Summary.Text;
        exposureCache.ContentHash = emergencyMessageContent.DSCHash;
        exposureCache.DEId = emergencyMessageContainer.DistributionCode;
        exposureCache.DESender = emergencyMessageContainer.SenderCode;

        if (contentHasXMLContent)
        {
          exposureCache.Title = syndicationItem.Title.Text;
          exposureCache.Link = ICNETSOAConstants.ICNETdatalinkToHTML + emergencyMessageContent.DSCHash.ToString(); ////syndicationItem.Links[0].Uri.ToString();//HACK

          if (syndicationItem.ElementExtensions.Count < 1)
          {
            this.AddGeoLocation(ref exposureCache, emergencyMessageContainer, containerHasTargetArea);
          }
          else
          {
            XElement whereElem = syndicationItem.ElementExtensions.ReadElementExtensions<XElement>("where", EDXLConstants.GeoRSSNamespace).FirstOrDefault();

            string s = string.Empty;
            if (whereElem != null)
            {
              //strip off georss:where element
              s = whereElem.FirstNode.NextNode.ToString();
            }

            //XmlReader xr = syndicationItem.ElementExtensions[0].GetReader();
            //string s;
            //s = xr.ReadInnerXml();

            // HACK HACK HACK
            if (s == string.Empty || s == "<georss:point xmlns:georss=\"http://www.georss.org/georss\"/>")
            {
            }
            else if (s.Contains("georss"))
            {
              XElement xe = XElement.Parse(s);
              exposureCache.Georss = xe;

              // LinQDB.InsertExposureWhere(DBItem.Id, xe);
            }
            else if (s.Contains("CircleByCenterPoint"))
            {
              this.AddCircle(ref exposureCache, s);
            }
            else if (s.Contains("Envelope"))
            {
              this.AddEnvelope(ref exposureCache, s);
            }
            else if (s.Contains("Polygon"))
            {
              this.AddPolygon(ref exposureCache, s);
            }
            else
            {
              XElement xe = XElement.Parse(s);
              exposureCache.Polygon = xe;

              // LinQDB.InsertExposureWhere(DBItem.Id, xe);
            }
          }

          
        }
        else 
        {
            if (contentObject.ContentDescription==null) 
            {
                // Make up a new title
                exposureCache.Title = emergencyMessageContainer.DistributionCode + " - " + emergencyMessageContainer.SenderCode + " - " + contentObject.NonXMLContent.MIMEType;
            }
            else{
                // Use the contentDescription as the title
                exposureCache.Title = contentObject.ContentDescription;
            }
            

          if (contentObject.NonXMLContent.URI != null)
          {
            exposureCache.Link = contentObject.NonXMLContent.URI.ToString();
          }
          else
          {
            exposureCache.Link = ICNETSOAConstants.ICNETdatalinkToMIME + emergencyMessageContent.DSCHash.ToString();
          }

          this.AddGeoLocation(ref exposureCache, emergencyMessageContainer, containerHasTargetArea);
        }

        

        //overwrite expiration time if DE has been cancelled
        if (emergencyMessageContainer.DistributionType.Equals(TypeValue.Cancel.ToString()))
        {
          exposureCache.ExpirationTime = emergencyMessageContainer.DateTimeSent;
        }
        else
        {
          String expiresStr = syndicationItem.ElementExtensions.ReadElementExtensions<String>("expire_time", "").FirstOrDefault();
          DateTime expiresDT;

          if (DateTime.TryParse(expiresStr, out expiresDT))
          {
            exposureCache.ExpirationTime = expiresDT;
          }
          else
          {
            exposureCache.ExpirationTime = emergencyMessageContainer.DateTimeSent.AddMinutes(ICNETSOAConstants.CacheClearDurationInMinutes);
          }
        }
        emergencyMessageContent.ExposureCaches.Add(exposureCache);
        this.AddExposureCategoryObjectsToExposureCacheObject(syndicationItem, exposureCache);
        this.AddExposureCacheRoleObjectsToExposureCache(contentObject, exposureCache);
        this.AddExposureCacheExtensionsToExposureCache(contentObject, exposureCache);
      }
    }

    /////// <summary>
    ///////   Returns exposure cache objects for a emergency message content object
    /////// </summary>
    /////// <param name="contentObject">EDXLDE object</param>
    /////// <param name="emergencyMessageContainer">Emergency message container object</param>
    ////private List<ExposureCache> GetExposureCacheObjectsFromEDXLDE(EDXLDE DEObj, EmergencyMessageContainer emergencyMessageContainer)
    ////{
    ////  EmergencyMessageContent emergencyMessageContent;
    ////  foreach (ContentObject co in DEObj.ContentObjects)
    ////  {
    ////    emergencyMessageContent = new EmergencyMessageContent();
    ////    emergencyMessageContent.Confidentiality = co.Confidentiality;
    ////    emergencyMessageContent.ContentDescription = co.ContentDescription;
    ////    emergencyMessageContent.IncidentDescription = co.IncidentDescription;
    ////    emergencyMessageContent.IncidentCode = co.IncidentID;

    ////    emergencyMessageContent.Type = co.XMLContent != null ? "XML" : "NonXML";
    ////    emergencyMessageContainer.EmergencyMessageContents.Add(emergencyMessageContent);

    ////    AddContentObjectValueListsToEmergencyMessageContent(co, emergencyMessageContent);
    ////    AddContentPayloadsToEmergencyMessageContent(co, emergencyMessageContent);
    ////    AddExposureCacheObjectsToEmergencyMessageContent(co, emergencyMessageContent, emergencyMessageContainer);
    ////  }

    ////  List<SyndicationItem> syndicationItems;
    ////  bool contentHasXMLContent = contentObject.XMLContent != null;
    ////  bool containerHasTargetArea = emergencyMessageContainer.TargetAreas.Count() > 0;

    ////  if (contentHasXMLContent)
    ////  {
    ////    syndicationItems = GeoRSSDal.GetXMLContentSyndicationItems(contentObject);
    ////  }
    ////  else
    ////  {
    ////    syndicationItems = GeoRSSDal.GetNonXMLContentSyndicationItems(contentObject);
    ////  }

    ////  foreach (SyndicationItem syndicationItem in syndicationItems)
    ////  {
    ////    ExposureCache exposureCache = new ExposureCache();
    ////    exposureCache.Author = syndicationItem.Authors[0].Name;
    ////    exposureCache.BaseUri = ICNETSOAConstants.ICNETbaseURI;
    ////    exposureCache.Content = ((TextSyndicationContent)syndicationItem.Content).Text;
    ////    exposureCache.Copyright = syndicationItem.Copyright.Text;
    ////    exposureCache.LastUpdatedTime = syndicationItem.LastUpdatedTime.DateTime;
    ////    exposureCache.Link = syndicationItem.Links[0].Uri.ToString();//HACK
    ////    exposureCache.PublishDate = syndicationItem.PublishDate.DateTime;
    ////    exposureCache.Summary = syndicationItem.Summary.Text;
    ////    exposureCache.Title = syndicationItem.Title.Text;

    ////    if (contentHasXMLContent)
    ////    {
    ////      if (syndicationItem.ElementExtensions.Count < 1)
    ////      {
    ////        AddGeoLocation(ref exposureCache, emergencyMessageContainer, containerHasTargetArea);
    ////      }
    ////      else
    ////      {
    ////        XmlReader xr = syndicationItem.ElementExtensions[0].GetReader();
    ////        string s;
    ////        s = xr.ReadInnerXml();
    ////        //HACK HACK HACK
    ////        if (s == "" || s == "<georss:point xmlns:georss=\"http://www.georss.org/georss\"/>")
    ////        {
    ////        }
    ////        else if (s.Contains("georss"))
    ////        {
    ////          XElement xe = XElement.Parse(s);
    ////          exposureCache.Georss = xe;
    ////          //          LinQDB.InsertExposureWhere(DBItem.Id, xe);
    ////        }
    ////        else if (s.Contains("CircleByCenterPoint"))
    ////        {
    ////          AddCircle(ref exposureCache, s);
    ////        }
    ////        else if (s.Contains("Envelope"))
    ////        {
    ////          AddEnvelope(ref exposureCache, s);
    ////        }
    ////        else if (s.Contains("Polygon"))
    ////        {
    ////          AddPolygon(ref exposureCache, s);
    ////        }
    ////        else
    ////        {
    ////          XElement xe = XElement.Parse(s);
    ////          exposureCache.Polygon = xe;
    ////          //          LinQDB.InsertExposureWhere(DBItem.Id, xe);
    ////        }
    ////      }
    ////    }
    ////    else //Non-XML Content
    ////    {
    ////      AddGeoLocation(ref exposureCache, emergencyMessageContainer, containerHasTargetArea);
    ////    }
    ////    emergencyMessageContent.ExposureCaches.Add(exposureCache);
    ////    AddExposureCategoryObjectsToExposureCacheObject(syndicationItem, exposureCache);
    ////    AddExposureCacheRoleObjectsToExposureCache(contentObject, exposureCache);
    ////  }
    ////}

    /// <summary>
    /// Add a circle to the exposure cache object
    /// </summary>
    /// <param name="expCache">In/Out Exposure Cache object</param>
    /// <param name="source">Source of Circle Information</param>
    private void AddCircle(ref ExposureCache expCache, string source)
    {
      int index, end, len;

      index = source.IndexOf("pos");
      index = source.IndexOf(">", index);
      index++;
      end = source.IndexOf(" ", index);
      len = end - index;
      string lat = source.Substring(index, len);
      end++;
      index = end;
      end = source.IndexOf("<", index);
      len = end - index;
      string lon = source.Substring(index, len);
      index = source.IndexOf("radius", index);
      index = source.IndexOf(">", index);
      index++;
      end = source.IndexOf("<", index);
      len = end - index;
      string radius = source.Substring(index, len);
      //radius = (Double.Parse(radius) * 1000).ToString();
      ////LinQDB.InsertExposureCircle(DBItem.Id, lat, lon, radius);
      expCache.Circle = lat + "," + lon + " " + radius;
    }

    /// <summary>
    /// Add an Envelope to the exposure cache object
    /// </summary>
    /// <param name="expCache">In/Out Exposure Cache object</param>
    /// <param name="source">Source of Envelope information</param>
    private void AddEnvelope(ref ExposureCache expCache, string source)
    {
      int index, end, len;
      index = source.IndexOf("lowerCorner");
      index = source.IndexOf(">", index);
      index++;
      end = source.IndexOf("<", index);
      len = end - index;
      string point1 = source.Substring(index, len);
      index = source.IndexOf("upperCorner", index);
      index++;
      index = source.IndexOf(">", index);
      index++;
      end = source.IndexOf("<", index);
      len = end - index;
      string point2 = source.Substring(index, len);
      index = point1.IndexOf(" ");
      double lat1 = double.Parse(point1.Substring(0, index));
      double lon1 = double.Parse(point1.Substring(index + 1));
      index = point2.IndexOf(" ");
      double lat2 = double.Parse(point2.Substring(0, index));
      double lon2 = double.Parse(point2.Substring(index + 1));
      double smalllat, biglat, smalllon, biglon;
      if (lat1 < lat2)
      {
        smalllat = lat1;
        biglat = lat2;
      }
      else
      {
        smalllat = lat2;
        biglat = lat1;
      }

      if (lon1 < lon2)
      {
        smalllon = lon1;
        biglon = lon2;
      }
      else
      {
        smalllon = lon2;
        biglon = lon1;
      }

      expCache.BoxLower = point1;
      expCache.BoxUpper = point2;
      expCache.Envelope = "POLYGON((" + biglon + ' ' + biglat + ',' + smalllon + ' ' + biglat + ',' + smalllon + ' ' + smalllat + ',' + biglon + ' ' + smalllat + ',' + biglon + ' ' + biglat + "))";
      ////LinQDB.InsertExposureEnvelope(DBItem.Id, biglat.ToString(), smalllat.ToString(), biglon.ToString(), smalllon.ToString());
    }

    /// <summary>
    /// Adds a polygon to the exposure cache object
    /// </summary>
    /// <param name="expCache">In/Out Exposure Cache object</param>
    /// <param name="source">Source of polygon information</param>
    private void AddPolygon(ref ExposureCache expCache, string source)
    {
      //int index, end, len;
      //index = source.IndexOf("posList");
      //while (index != -1)
      //{
      //  index = source.IndexOf(">", index);
      //  index++;
      //  end = source.IndexOf("<", index);
      //  len = end - index;
      //  string poslist = source.Substring(index, len);
      //  StringBuilder sb = new StringBuilder();
      //  char[] seperator = new char[] { ' ' };
      //  string[] value = poslist.Split(seperator);
      //  for (int i = value.Length - 2; i >= 0; i++)
      //  {
      //    sb.Append(value[i]);
      //    sb.Append(" ");
      //    if (i % 2 == 1)
      //    {
      //      i -= 4;
      //    }
      //  }

      //  source = source.Replace(poslist, sb.ToString().Trim());
      //  index = end + 1;
      //  index = source.IndexOf("posList", index);
      //  index++;
      //  index = source.IndexOf("posList", index);
      //}

      XElement xe = XElement.Parse(source);
      expCache.Polygon = xe;
      ////LinQDB.InsertExposureWhere(DBItem.Id, xe);
    }

    /// <summary>
    /// Uses parent TargetArea geo info or creates a point at 0, 0 (lat, lon) for the exposure cache
    /// </summary>
    /// <param name="expCache">Exposure Cache object</param>
    /// <param name="emergencyMessagesContainer">Emergency Message Container object</param>
    /// <param name="containerHasTargetArea">Whether or not DEObj has a target area</param>
    private void AddGeoLocation(ref ExposureCache expCache, EmergencyMessageContainer emergencyMessagesContainer, bool containerHasTargetArea)
    {
      if (containerHasTargetArea)
      {
        if (emergencyMessagesContainer.TargetAreas[0].Circle != null)
        {
          expCache.Circle = emergencyMessagesContainer.TargetAreas[0].Circle;
        }
        else
        {
          if (emergencyMessagesContainer.TargetAreas[0].Location != null)
          {
            EDXLSQLDataContext linqDB = new EDXLSQLDataContext(ICNETSOAConstants.ICNETConnectionString);
            expCache.Polygon = linqDB.PolygonToGml(emergencyMessagesContainer.TargetAreas[0].Location);
          }
          else
          {
            expCache.Circle = "0,0 0";
          }
        }
      }
      else
      {
        expCache.Circle = "0,0 0";
      }
    }

    /// <summary>
    ///   Adds exposure category objects to an exposure cache object
    /// </summary>
    /// <param name="syndicationItem">A syndication item from which an exposure cache object was created</param>
    /// <param name="exposureCache">An exposure cache object</param>
    private void AddExposureCategoryObjectsToExposureCacheObject(SyndicationItem syndicationItem, ExposureCache exposureCache)
    {
      ExposureCategory exposureCategory;
      foreach (SyndicationCategory syndicationCategory in syndicationItem.Categories)
      {
        exposureCategory = new ExposureCategory();
        exposureCategory.Label = syndicationCategory.Label;
        exposureCategory.Name = syndicationCategory.Name;
        exposureCategory.Scheme = syndicationCategory.Scheme;
        exposureCache.ExposureCategories.Add(exposureCategory);
      }
    }

    /// <summary>
    ///   Adds exposure cache role objects to an exposure cache object
    /// </summary>
    /// <param name="contentObject">Content object serialized from an EDXL document</param>
    /// <param name="exposureCache">An exposure cache object</param>
    private void AddExposureCacheRoleObjectsToExposureCache(ContentObject contentObject, ExposureCache exposureCache)
    {
      ExposureCacheRole exposureCacheRole;
      foreach (ValueList consumerRole in contentObject.ConsumerRole)
      {
        //change our internal routing roles back, so the exposure cache 
        //has the correct routing role

        //create a list of distinct roles if there are duplicates
        //in the same value list...otherwise multiple entries will
        //be returned when a feed is requested for that role and uri
        List<string> distinctRoles = new List<string>();
        foreach (string value in consumerRole.Value)
        {
          //if the list doesn't contain the new value, add it
          if (!distinctRoles.Contains(value))
          {
            distinctRoles.Add(value);
          }
        }

        //create a role entry for each distinct role
        foreach (string value in distinctRoles)
        {
          exposureCacheRole = new ExposureCacheRole();
          exposureCacheRole.ListURI = consumerRole.ValueListURN.Replace("icnet-http", "http");
          exposureCacheRole.Value = value;
          exposureCache.ExposureCacheRoles.Add(exposureCacheRole);
        }
      }
    }
    /// <summary>
    ///   Adds exposure cache extention objects to an exposure cache object
    /// </summary>
    /// <param name="contentObject">Content object serialized from an EDXL document</param>
    /// <param name="exposureCache">An exposure cache object</param>
    private void AddExposureCacheExtensionsToExposureCache(ContentObject contentObject, ExposureCache exposureCache)
    {
      //determine if the content object has XML
      if (contentObject.XMLContent != null)
      {
        CoTWrapper cotmsg;

        int index = -1;
        index = contentObject.IndexOfNamedKeywordList(EDXLConstants.ContentKeywordListName);
        if (contentObject.XMLContent != null && contentObject.ContentKeyword != null && index != -1)
        {
          foreach (XElement doc in contentObject.XMLContent.EmbeddedXMLContent)
          {

            //currently we only support CoT xml
            if (contentObject.ContentKeyword[index].Value.Contains("CoTEvent"))
            {
              cotmsg = new CoTWrapper();
              XmlDocument xdoc = new XmlDocument();
              using (XmlReader xr = doc.CreateReader())
              {
                xdoc.Load(xr);
              }

              cotmsg.ReadXML(xdoc.FirstChild);

              ICotDetailComponent details = cotmsg.CoTEvent.Detail.GetFirstElement("__icnet");
              if (details != null)
              {
                XmlNode node = details.XmlNode.SelectSingleNode("beacon");
                if (node != null)
                {
                  AddExposureCacheExtensionFromAttribute(node, "number", exposureCache);
                  AddExposureCacheExtensionFromAttribute(node, "battery", exposureCache);
                  AddExposureCacheExtensionFromAttribute(node, "signal", exposureCache);
                  AddExposureCacheExtensionFromAttribute(node, "batteryState", exposureCache);
                }

                node = details.XmlNode.SelectSingleNode("icall");;
                if (node != null)
                {
                  AddExposureCacheExtensionFromAttribute(node, "editable", exposureCache);
                }
              }
            }
          }
        }
      }
    }

    /// <summary>
    /// Adds a new ExposureCacheExtension from a specified XML attribute
    /// </summary>
    /// <param name="node">XML node to get attribute from</param>
    /// <param name="attributeName">Attribute name to look for</param>
    /// <param name="exposureCache">Exposure Cache object to add the extention to</param>
    private void AddExposureCacheExtensionFromAttribute(XmlNode node, string attributeName, ExposureCache exposureCache)
    {
      XmlAttribute attr = node.Attributes[attributeName];

      if (attr != null)
      {
        ExposureCacheExtension ext = new ExposureCacheExtension();
        ext.Key = attributeName;
        ext.Value = attr.Value;

        exposureCache.ExposureCacheExtensions.Add(ext);
      }
    }

    #endregion
  }
}
